
package com.eworking.eim.message;

import java.util.Iterator;

import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Message.Body;
import org.json.JSONArray;
import org.json.JSONObject;

import com.eworking.eim.encrypt.EIMCryptoUtils;
import com.eworking.eim.message.body.CmdMessageBody;
import com.eworking.eim.message.body.ImageMessageBody;
import com.eworking.eim.message.body.LocationMessageBody;
import com.eworking.eim.message.body.TextMessageBody;
import com.eworking.eim.message.body.VideoMessageBody;
import com.eworking.eim.message.body.VoiceMessageBody;
import com.eworking.eim.user.EIMUser;
import com.eworking.eim.util.EIMLog;

public class EIMMessageEncoder {
    
    /** 日志输出标记 */
    private static final String _TAG = EIMMessageEncoder.class.getSimpleName();
    
    /** 消息类型 - 文本 */
    private static final String _ATTR_TYPE_TXT = "txt";
    
    /** 消息类型 - 图片 */
    private static final String _ATTR_TYPE_IMG = "img";
    
    /** 消息类型 - 语音 */
    private static final String _ATTR_TYPE_VOICE = "audio";
    
    /** 消息类型 - 视频 */
    private static final String _ATTR_TYPE_VIDEO = "video";
    
    /** 消息类型 - 地理位置 */
    private static final String _ATTR_TYPE_LOCATION = "location";
    
    /** 消息类型 - 命名 */
    private static final String _ATTR_TYPE_CMD = "cmd";
    
    /**
     * 将Message信息转换为JSON字符串
     * 
     * @param message 消息实体
     * @param isSaveLocal 是否保留本地信息
     * @return
     */
    public static String convertMsgToJSON(EIMMessage message, boolean isSaveLocal) {
        StringBuffer strJSON = new StringBuffer();
        strJSON.append("{");
        strJSON.append("\"from\":\"" + message.getFrom() + "\",");
        strJSON.append("\"to\":\"" + message.getTo() + "\",");
        strJSON.append("\"bodies\":[{");
        if (message.type == EIMMessage.Type.TXT)
            // 文本消息
            addTxtBody(strJSON, message);
        else if (message.type == EIMMessage.Type.IMAGE)
            // 图文消息
            addImageBody(strJSON, message, isSaveLocal);
        else if (message.type == EIMMessage.Type.VOICE)
            // 语音消息
            addVoiceBody(strJSON, message, isSaveLocal);
        else if (message.type == EIMMessage.Type.LOCATION)
            // 地理位置消息
            addLocationBody(strJSON, message);
        else if (message.type == EIMMessage.Type.CMD)
            // 命令消息
            addCmdBody(strJSON, message);
        else if (message.type == EIMMessage.Type.VIDEO)
            // 视频消息
            addVideoBody(strJSON, message, isSaveLocal);
        strJSON.append("}]");
        if (message.attributes != null)
            addExtAttr(strJSON, message);
        strJSON.append("}");
        strJSON.toString();
        return strJSON.toString();
    }
    
    /**
     * 转换文本类型的消息
     * 
     * @param strJSON
     * @param message 消息实体
     */
    private static void addTxtBody(StringBuffer strJSON, EIMMessage message) {
        strJSON.append("\"type\":\"" + _ATTR_TYPE_TXT + "\",");
        TextMessageBody textMessageBody = (TextMessageBody) message.messageBody;
        strJSON.append("\"msg\":\"" + textMessageBody.message + "\"");
    }
    
    /**
     * 转换图片消息
     * 
     * @param strJSON
     * @param message
     * @param isSaveLocal
     */
    private static void addImageBody(StringBuffer strJSON, EIMMessage message, boolean isSaveLocal) {
        strJSON.append("\"type\":\"" + _ATTR_TYPE_IMG + "\",");
        ImageMessageBody imageMessageBody = (ImageMessageBody) message.getMessageBody();
        strJSON.append("\"url\":\"" + imageMessageBody.remoteUrl + "\",");
        // 是否需要保存客户端本地信息
        if (isSaveLocal) {
            strJSON.append("\"localurl\":\"" + imageMessageBody.localUrl + "\",");
        }
        strJSON.append("\"filename\":\"" + imageMessageBody.fileName + "\",");
        strJSON.append("\"thumb\":\"" + imageMessageBody.thumbnailUrl + "\",");
        strJSON.append("\"secret\":\"" + imageMessageBody.secret + "\"");
        if (imageMessageBody.thumbnailSecret != null) {
            strJSON.append(",\"thumb_secret\":\"" + imageMessageBody.thumbnailSecret + "\"");
        }
    }
    
    /**
     * 转换语音消息
     * 
     * @param strJSON
     * @param message
     * @param isSaveLocal
     */
    private static void addVoiceBody(StringBuffer strJSON, EIMMessage message, boolean isSaveLocal) {
        strJSON.append("\"type\":\"" + _ATTR_TYPE_VOICE + "\",");
        VoiceMessageBody localVoiceMessageBody = (VoiceMessageBody) message.getMessageBody();
        strJSON.append("\"url\":\"" + localVoiceMessageBody.remoteUrl + "\",");
        if (isSaveLocal) {
            strJSON.append("\"localurl\":\"" + localVoiceMessageBody.localUrl + "\",");
        }
        strJSON.append("\"filename\":\"" + localVoiceMessageBody.fileName + "\",");
        strJSON.append("\"length\":" + localVoiceMessageBody.length + ",");
        strJSON.append("\"secret\":\"" + localVoiceMessageBody.secret + "\"");
    }
    
    /**
     * 转换地理位置消息
     * 
     * @param strJSON
     * @param message
     */
    private static void addLocationBody(StringBuffer strJSON, EIMMessage message) {
        strJSON.append("\"type\":\"" + _ATTR_TYPE_LOCATION + "\",");
        LocationMessageBody localLocationMessageBody = (LocationMessageBody) message.getMessageBody();
        strJSON.append("\"addr\":\"" + localLocationMessageBody.address + "\",");
        strJSON.append("\"lat\":" + localLocationMessageBody.latitude + ",");
        strJSON.append("\"lng\":" + localLocationMessageBody.longitude);
    }
    
    /**
     * 转换命令类型消息
     * 
     * @param strJSON
     * @param message
     */
    private static void addCmdBody(StringBuffer strJSON, EIMMessage message) {
        strJSON.append("\"type\":\"" + _ATTR_TYPE_CMD + "\",");
        CmdMessageBody localCmdMessageBody = (CmdMessageBody) message.getMessageBody();
        strJSON.append("\"action\":\"" + localCmdMessageBody.action + "\",");
        strJSON.append("\"param\":[");
        if (localCmdMessageBody.params != null) {
            String[] arrayOfString;
            int j = (arrayOfString = localCmdMessageBody.params).length;
            for (int i = 0; i < j; ++i) {
                String str = arrayOfString[i];
                strJSON.append("\"" + str + "\",");
            }
        }
        strJSON.append("]");
    }
    
    /**
     * 转换视频消息
     * 
     * @param strJSON
     * @param message
     * @param isSaveLocal
     */
    private static void addVideoBody(StringBuffer strJSON, EIMMessage message, boolean isSaveLocal) {
        strJSON.append("\"type\":\"" + _ATTR_TYPE_VIDEO + "\",");
        VideoMessageBody localVideoMessageBody = (VideoMessageBody) message.getMessageBody();
        strJSON.append("\"url\":\"" + localVideoMessageBody.remoteUrl + "\",");
        if (isSaveLocal) {
            strJSON.append("\"localurl\":\"" + localVideoMessageBody.localUrl + "\",");
            strJSON.append("\"thumblocalurl\":\"" + localVideoMessageBody.localThumb + "\",");
        }
        strJSON.append("\"filename\":\"" + localVideoMessageBody.fileName + "\",");
        strJSON.append("\"thumb\":\"" + localVideoMessageBody.thumbnailUrl + "\",");
        strJSON.append("\"length\":" + localVideoMessageBody.length + ",");
        strJSON.append("\"filelength\":" + localVideoMessageBody.file_length + ",");
        strJSON.append("\"secret\":\"" + localVideoMessageBody.secret + "\"");
        if (localVideoMessageBody.thumbnailSecret != null) {
            strJSON.append(",\"thumb_secret\":\"" + localVideoMessageBody.thumbnailSecret + "\"");
        }
    }
    
    /**
     * 转换扩展属性
     * 
     * @param strJSON
     * @param message
     */
    private static void addExtAttr(StringBuffer strJSON, EIMMessage message) {
        strJSON.append(",");
        strJSON.append("\"ext\":{");
        int iCount = 1;
        Iterator<String> iterator = message.attributes.keySet().iterator();
        while (iterator.hasNext()) {
            String attrName = iterator.next();
            strJSON.append("\"" + attrName + "\":");
            Object objValue = message.attributes.get(attrName);
            if (objValue instanceof String) {
                String attrValue = String.valueOf(objValue);
                if (attrValue.startsWith("{") && attrValue.endsWith("}") && attrValue.contains(":")) {
                    strJSON.append(attrValue);
                } else {
                    strJSON.append("\"" + objValue + "\"");
                }
            } else if (objValue instanceof Boolean) {
                Boolean attrValue = (Boolean) objValue;
                if (attrValue.booleanValue()) {
                    strJSON.append("true");
                } else {
                    strJSON.append("false");
                }
            } else {
                Integer attrValue = (Integer) objValue;
                strJSON.append(attrValue.toString());
            }
            if (iCount < message.attributes.size()) {
                strJSON.append(",");
            }
            ++iCount;
        }
        strJSON.append("}");
    }
    
    /**
     * 将JSON字符串的消息内容转换为Message实体对象
     * 
     * @param strJSONMsg JSON字符串的消息内容
     * @return
     */
    public static EIMMessage convertJSONToMessage(String strJSONMsg) {
        try {
            JSONObject jsonObject = new JSONObject(strJSONMsg);
            // 消息发送人实体
            EIMUser fromUser = new EIMUser(jsonObject.getString("from"));
            // 消息接收人实体
            EIMUser toUser = new EIMUser(jsonObject.getString("to"));
            // 消息内容集合
            JSONArray bodyJsonArray = jsonObject.getJSONArray("bodies");
            if (bodyJsonArray.length() < 1) {
                EIMLog.d("encoder", "wrong msg without body");
                return null;
            }
            JSONObject bodyJsonObject = bodyJsonArray.getJSONObject(0);
            // 消息类型
            String type = bodyJsonObject.getString("type");
            EIMMessage message = null;
            // 文本消息
            if (type.equals(_ATTR_TYPE_TXT)) {
                message = new EIMMessage(EIMMessage.Type.TXT);
                // 构造消息内容实体
                TextMessageBody textMessageBody = new TextMessageBody(bodyJsonObject.getString("msg"));
                message.setMessageBody(textMessageBody);
            } else {
                // 图片消息
                if (type.equals(_ATTR_TYPE_IMG)) {
                    message = new EIMMessage(EIMMessage.Type.IMAGE);
                    String strRUL = bodyJsonObject.getString("url");
                    String filename = bodyJsonObject.getString("filename");
                    String thumb = bodyJsonObject.getString("thumb");
                    // 构造消息内容实体
                    ImageMessageBody imageMessageBody = new ImageMessageBody(filename, strRUL, thumb);
                    if (bodyJsonObject.has("localurl")) {
                        imageMessageBody.localUrl = bodyJsonObject.getString("localurl");
                    }
                    if (bodyJsonObject.has("secret")) {
                        imageMessageBody.setSecret(bodyJsonObject.getString("secret"));
                    }
                    if (bodyJsonObject.has("thumb_secret")) {
                        imageMessageBody.setThumbnailSecret(bodyJsonObject.getString("thumb_secret"));
                    }
                    // 设置消息内容实体
                    message.setMessageBody(imageMessageBody);
                } else if (type.equals("video")) {
                    // 视频消息
                    message = new EIMMessage(EIMMessage.Type.VIDEO);
                    String strURL = bodyJsonObject.getString("url");
                    String filename = bodyJsonObject.getString("filename");
                    String thumb = bodyJsonObject.getString("thumb");
                    int length = bodyJsonObject.getInt("length");
                    VideoMessageBody videoMessageBody = new VideoMessageBody(filename, strURL, thumb, length);
                    if (bodyJsonObject.has("localurl")) {
                        videoMessageBody.localUrl = bodyJsonObject.getString("localurl");
                    }
                    if (bodyJsonObject.has("filelength")) {
                        videoMessageBody.file_length = bodyJsonObject.getLong("filelength");
                    }
                    if (bodyJsonObject.has("secret")) {
                        videoMessageBody.setSecret(bodyJsonObject.getString("secret"));
                    }
                    if (bodyJsonObject.has("thumb_secret")) {
                        videoMessageBody.setThumbnailSecret(bodyJsonObject.getString("thumb_secret"));
                    }
                    message.setMessageBody(videoMessageBody);
                } else {
                    if (type.equals("audio")) {
                        // 语音消息
                        message = new EIMMessage(EIMMessage.Type.VOICE);
                        String strURL = bodyJsonObject.getString("url");
                        String filename = bodyJsonObject.getString("filename");
                        int length = bodyJsonObject.getInt("length");
                        VoiceMessageBody voiceMessageBody = new VoiceMessageBody(filename, strURL, length);
                        if (bodyJsonObject.has("localurl"))
                            voiceMessageBody.localUrl = bodyJsonObject.getString("localurl");
                        if (bodyJsonObject.has("secret"))
                            voiceMessageBody.setSecret(bodyJsonObject.getString("secret"));
                        message.setMessageBody(voiceMessageBody);
                    } else if (type.equals("loc")) {
                        // 地理位置消息
                        message = new EIMMessage(EIMMessage.Type.LOCATION);
                        String addr = bodyJsonObject.getString("addr");
                        double lat = bodyJsonObject.getDouble("lat");
                        double lng = bodyJsonObject.getDouble("lng");
                        LocationMessageBody locationMessageBody = new LocationMessageBody(addr, lat, lng);
                        message.setMessageBody(locationMessageBody);
                    } else if (type.equals("cmd")) {
                        // 命令消息
                        message = new EIMMessage(EIMMessage.Type.CMD);
                        String[] params = null;
                        if (bodyJsonObject.has("param")) {
                            JSONArray paramJsonArray = bodyJsonObject.getJSONArray("param");
                            params = new String[paramJsonArray.length()];
                            for (int i = 0; i < params.length; ++i) {
                                params[i] = paramJsonArray.optString(i);
                            }
                        }
                        CmdMessageBody cmdMessageBody = new CmdMessageBody(bodyJsonObject.getString("action"), params);
                        message.setMessageBody(cmdMessageBody);
                    }
                }
            }
            if (message != null) {
                message.from = fromUser;
                message.to = toUser;
            }
            // 消息扩展属性
            if (bodyJsonObject.has("ext")) {
                JSONObject extJsonObject = bodyJsonObject.getJSONObject("ext");
                @SuppressWarnings("unchecked")
                Iterator<String> iterator = extJsonObject.keys();
                while (iterator.hasNext()) {
                    String key = iterator.next();
                    Object value = extJsonObject.get(key);
                    if (value instanceof String) {
                        message.setAttribute(key, (String) value);
                    } else if (value instanceof Integer) {
                        message.setAttribute(key, ((Integer) value).intValue());
                    } else if (value instanceof Boolean) {
                        message.setAttribute(key, ((Boolean) value).booleanValue());
                    } else if (value instanceof JSONObject) {
                        String localObject4 = ((JSONObject) value).toString();
                        message.setAttribute(key, localObject4);
                    } else {
                        EIMLog.e("msg", "unknow additonal msg attr:" + value.getClass().getName());
                    }
                }
            }
            return message;
        } catch (Exception localException) {
            return null;
        }
    }
    
    /**
     * 对消息内容进行解密
     * 
     * @param message 原始消息实体
     * @return
     */
    public static EIMMessage decryptMessage(Message message) {
        if (message.getExtension("encrypt", "jabber:client") != null) {
            EIMLog.d("encoder", "it is encrypted message, decripting");
            try {
                // 密文消息内容
                String msgBody = message.getBody();
                // 对消息内容进行解密
                String strExpMsgBody = EIMCryptoUtils.getInstance().decryptBase64String(msgBody);
                // 获取所有消息体
                Iterator<Body> iterator = message.getBodies().iterator();
                while (iterator.hasNext()) {
                    Message.Body localBody = (Message.Body) iterator.next();
                    message.removeBody(localBody);
                }
                // 将消息内容设置为明文内容
                message.setBody(strExpMsgBody);
            } catch (Exception e) {
                EIMLog.d(_TAG, "解密消息内容失败，" + e.getMessage());
            }
        }
        // 明文消息内容
        String strExpMsgBody = message.getBody();
        if ((!(strExpMsgBody.startsWith("{"))) || (!(strExpMsgBody.endsWith("}")))) {
            EIMLog.d("encoder", "msg not in json format, ignore");
            return null;
        }
        EIMMessage eimMessage = convertJSONToMessage(strExpMsgBody);
        if (eimMessage == null) {
            EIMLog.e("encoder", "wrong message format:" + message.toXML());
            return null;
        }
        eimMessage.direct = EIMMessage.Direct.RECEIVE;
        eimMessage.msgId = message.getPacketID();
        eimMessage.status = EIMMessage.Status.CREATE;
        eimMessage.unread = true;
        return eimMessage;
    }
}
